package org.dding.core.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.AndFileFilter;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.io.filefilter.NotFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;

public class FileExtUtils {

	/**
	 * Dump the contents of a JarArchive to the dpecified destination.
	 */
	public static void extractJar(File jarFile, File dest) throws IOException {
		if (!dest.exists()) {
			dest.mkdirs();
		}
		if (!dest.isDirectory()) {
			throw new IOException("Destination must be a directory.");
		}
		JarInputStream jin = new JarInputStream(new FileInputStream(jarFile));
		byte[] buffer = new byte[1024];

		ZipEntry entry = jin.getNextEntry();
		while (entry != null) {
			String fileName = entry.getName();
			if (fileName.charAt(fileName.length() - 1) == '/') {
				fileName = fileName.substring(0, fileName.length() - 1);
			}
			if (fileName.charAt(0) == '/') {
				fileName = fileName.substring(1);
			}
			if (File.separatorChar != '/') {
				fileName = fileName.replace('/', File.separatorChar);
			}
			File file = new File(dest, fileName);
			if (entry.isDirectory()) {
				// make sure the directory exists
				file.mkdirs();
				jin.closeEntry();
			} else {
				// make sure the directory exists
				File parent = file.getParentFile();
				if (parent != null && !parent.exists()) {
					parent.mkdirs();
				}

				// dump the file
				OutputStream out = new FileOutputStream(file);
				int len = 0;
				while ((len = jin.read(buffer, 0, buffer.length)) != -1) {
					out.write(buffer, 0, len);
				}
				out.flush();
				out.close();
				jin.closeEntry();
				file.setLastModified(entry.getTime());
			}
			entry = jin.getNextEntry();
		}
		/*
		 * Explicity write out the META-INF/MANIFEST.MF so that any headers such
		 * as the Class-Path are see for the unpackaged jar
		 */
		Manifest mf = jin.getManifest();
		if (mf != null) {
			File file = new File(dest, "META-INF/MANIFEST.MF");
			File parent = file.getParentFile();
			if (parent.exists() == false) {
				parent.mkdirs();
			}
			OutputStream out = new FileOutputStream(file);
			mf.write(out);
			out.flush();
			out.close();
		}
		jin.close();
	}

	public static Manifest getManifest(File jarFile) throws IOException {
		return new JarFile(jarFile).getManifest();
	}

	public static String toPath(String name) {
		return (name == null) ? null : name.replace(".", File.separator);
	}

	/**
	 * 从文件或目录中搜索所有的 class 文件
	 * 
	 * @param file
	 *            file
	 * @return classname=>class bytes
	 * @throws IOException
	 *             ioexception
	 */
	public static Map<String, byte[]> getClassBytesMap(File file)
			throws IOException {
		Map<String, byte[]> contents = new HashMap<String, byte[]>();
		if (file.isDirectory()) {
			AndFileFilter and = new AndFileFilter();
			and.addFileFilter(new SuffixFileFilter(new String[] { ".class" }));
			and.addFileFilter(new NotFileFilter(new NameFileFilter(
					"package-info.class")));
			Collection<File> files = FileUtils.listFiles(file, and,TrueFileFilter.INSTANCE);
			for (File _file : files) {
				String className = _file.getPath().substring(
						file.getPath().length() + 1);
				className = className.replaceFirst(".class$", "");
				className = className.replace(File.separatorChar, '.');
				byte[] content = IOUtils
						.toByteArray(new FileInputStream(_file));
				contents.put(className, content);
			}
		} else {
			JarFile jarFile = new JarFile(file);
			Enumeration<JarEntry> enu = jarFile.entries();
			try {
				while (enu.hasMoreElements()) {
					JarEntry entry = enu.nextElement();
					if (entry.isDirectory()
							|| !entry.getName().endsWith(".class")
							|| entry.getName().endsWith("package-info.class")) {
						continue;
					}
					String className = entry.getName();
					className = className.replaceFirst(".class$", "");
					className = className.replace('/', '.');
					InputStream in = null;
					try {
						in = new BufferedInputStream(
								jarFile.getInputStream(entry));
						byte[] content = IOUtils.toByteArray(in);
						contents.put(className, content);
					} finally {
						if (in != null) {
							in.close();
						}
					}
				}
			} finally {
				jarFile.close();
			}
		}
		return Collections.unmodifiableMap(contents);
	}
}
